

/*
 * log.c
 *
 *  编写时间: 2021/7/23
 *  编写者: wmh
 *
 */
#include "log.h"

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdarg.h>
#include <fcntl.h>
#include <time.h>

#if LOG_FILE_BAK
#include <sys/ipc.h>
#include "sem.h"
#endif

/************* 模块私有配置变量****************/
LOG_L_TYPE log_module_level;  /* 模块打印等级 */
char *log_module_prefix;      /* 模块前缀 */
char *log_module_file_prefix; /* 模块文件名前缀 */

/**********************************************/
char dir[128];
FILE *log_fp;

#if LOG_FILE_BAK
int semid;
#endif

/*
        配置模块私有配置选项函数
        log_module_init(LOG_MODE_FILE, LOG_L_INF, "test", "");
    */
void log_module_init(LOG_L_TYPE log_module_level_p, char *log_module_prefix_p, char *log_module_file_prefix_p)
{
    log_module_level = log_module_level_p;
    log_module_prefix = log_module_prefix_p;
    log_module_file_prefix = log_module_file_prefix_p;

    mkdir(COM_LOG_FILE_DIR, 0777);

    /*拼出日志文件全路径 多进程存储到同一文件时也能同步找到*/
    sprintf(dir, "%s%s%s%s", COM_LOG_FILE_DIR, COM_LOG_FILE_PREFIX, log_module_file_prefix, COM_LOG_FILE_POSTFIX);

    /*这个初始化的唯一的key，自己把握，可以以文件唯一性*/
#if LOG_FILE_BAK
    key_t ftok_key;
    char *key_file_name = COM_LOG_FILE_DIR "/sem_file";

    creat(key_file_name, 0755);

    if (0 == access(key_file_name, F_OK))
    {
        ftok_key = ftok(key_file_name, 0);
    }
    else
    {
        ftok_key = 1234;
    }

    semid = sem_init(ftok_key);
#endif
}

/*检查日志文件大小，超过设置大小，就备份*/
#if LOG_FILE_BAK
void file_check()
{

    char bak_file_path[128];
    char time_buffer[80];
    time_t rawtime;

    struct tm *info;
    unsigned int index = 0;

    struct stat statbuf;
    statbuf.st_size = 0;

    if (0 == access(dir, F_OK))
    {
        stat(dir, &statbuf);

        if (statbuf.st_size >= LOG_MAX_FileSize)
        {
            if (log_fp > stderr)
            {
                fclose(log_fp);
                log_fp = NULL;
            }

            /*先确定备份目标文件名称路径bak_file_path，默认与日志文件同目录*/
            /*bak_file_path添加保存时的时间信息*/
            time(&rawtime);
            info = localtime(&rawtime);
            strftime(time_buffer, sizeof(time_buffer), "_%Y%m%d_%H%M%S", info);

            sprintf(bak_file_path, "%s%s%s%s%s", COM_LOG_FILE_DIR, COM_LOG_FILE_PREFIX, log_module_file_prefix,
                    time_buffer, COM_LOG_FILE_POSTFIX);

            /*日志文件上限小，或者日志存储过快，导致同秒就存满一个新的日志，就加序号*/
            if (0 == access(bak_file_path, F_OK))
            {
                /*欲备份文件名存在*/
                while (1)
                {
                    sprintf(bak_file_path, "%s%s%s%s_%d%s", COM_LOG_FILE_DIR, COM_LOG_FILE_PREFIX,
                            log_module_file_prefix,
                            time_buffer, index, COM_LOG_FILE_POSTFIX);

                    if (0 == access(bak_file_path, F_OK))
                    {
                        index++;
                    }
                    else
                    {
                        break;
                    }
                }
            }

            /* 如果 newpath 已经存在，它将被原子替换*/
            if (0 == access(dir, F_OK))
            {
                if (-1 == rename(dir, bak_file_path))
                {
                    LOG_PRINT_ERR("rename %s to %s \n", dir, bak_file_path);
                }
            }
        }
    }
}
#endif

void LOG(LOG_L_TYPE enlog_level, const char *fmt, ...)
{

    /*判断日志等级*/
    if (log_module_level < enlog_level)
    {
        return;
    }

    char write_buf_header[512];
    char write_buf_msg[256];
    char time_buffer[64];

    time_t rawtime;

    struct tm *info;
    time(&rawtime);
    info = localtime(&rawtime);
    strftime(time_buffer, sizeof(time_buffer), "%Y/%m/%d %H:%M:%S", info);

    /*1.日志头部*/
    sprintf(write_buf_header, "[%s] ", time_buffer);

    /*2.日志消息*/
    va_list ap;

    va_start(ap, fmt);
    vsprintf(write_buf_msg, fmt, ap);
    va_end(ap);

    /*3.合并头部和消息*/
    strcat(write_buf_header, write_buf_msg);

    // 4.输出
    /*看前加锁，给别的程序加锁*/
#if LOG_FILE_BAK
    sem_p(semid, 0);
    file_check();
#endif

    log_fp = fopen(dir, "a");

    if (NULL == log_fp)
    {
        LOG_PRINT_ERR("%s open failed , print to stdout\n", dir);
        return;
    }

    // fprintf(log_fp, write_buf_header, strlen(write_buf_header));
    fwrite(write_buf_header, 1, strlen(write_buf_header), log_fp);

    //文件关闭
    fclose(log_fp);
    log_fp = NULL;

    //解锁
#if LOG_FILE_BAK
    sem_v(semid, 0);
#endif
}
